from odoo import http, _
from odoo.http import request
from datetime import datetime
import logging
from collections import defaultdict

_logger = logging.getLogger(__name__)


class MrpProductBomApi(http.Controller):
    @http.route('/api/mrp/product/post', type='json', auth='user', methods=['POST'], csrf=False)
    def product_bom_post(self, **kwargs):
        # with request.env.cr.savepoint():
        params = request.params
        fields = ['Impedance_diagram', 'stack_up_image', 'cutting_drawing_image']
        _logger.info('jmi 传参: %s' % params)
        if not request.env.user.has_group('sb_engineering.group_shy_engineering_user'):
            result = {'code': 1, 'msg': '此接接口需要获得工程权限'}
            params = self.remove_fields_recursive(params, fields)
            self.create_log_record('fail', params, result)
            return result
        try:
            code = self.recursion_checkout_bom([params])
        except Exception as e:
            # 去除图片
            params = self.remove_fields_recursive(params, fields)
            self.create_log_record('fail', params, str(e))
            return {'code': 1, 'msg': str(e), 'data': False}
        try:
            if code.get('code') != 200:
                raise Exception(code)
        except Exception as e:
            # 去除图片
            params = self.remove_fields_recursive(params, fields)
            self.create_log_record('fail', params, code)
            return code
        # JMI sale 校验
        # product_code = request.params.get('product_code')
        # product_template = request.env['product.template'].search([('default_code', '=', product_code)], limit=1)
        # order_line = request.env['sale.order.line'].search([('product_template_id', '=', product_template.id)], limit=1)
        # if order_line:
        #     order_id = order_line.order_id
        #     sb_workflow_item_ids = order_id.sb_workflow_item_ids
        #     # 审批完成订单的物料不可修改, 回滚
        #     if sb_workflow_item_ids and order_id.sb_workflow_state == 'approve':
        #         request.env.cr.rollback()
        #         return {
        #             'code': 1,
        #             'msg': 'Materials approved for completion of an order cannot be modified',
        #             'data': False
        #         }
        #     if sb_workflow_item_ids and order_id.sb_workflow_state == 'approval':
        #         # 直接将订单审批完成
        #         sb_workflow_item = sb_workflow_item_ids.filtered(lambda x: x.state == 'approval' and x.name=='工程审批')
        #         for record in sb_workflow_item:
        #             request.env['workflow.check.wizard'].create([{'sb_workflow_item_id': record.id,
        #                                                           'planned_receiving_date': False,
        #                                                           'approval_instructions': '审批通过'}]).approval_pass()
        # 处理 预设BOM
        _logger.info('jmi 完成')
        # else:
        #     request.env.cr.commit()
        idx = params.get('id')
        if idx and request.env['ir.config_parameter'].sudo().get_param('presets_bom_enable') == 'True':
            # 检查预设BOM, 新增预设BOM差异表
            # 获取成品半成品BOM信息
            def get_product_bom(in_params):
                data = {
                    'id': in_params.get('id'),
                    'product_name': in_params.get('product_name'),
                    'product_code': in_params.get('product_code'),
                    'param_line_ids': in_params.get('param_line_ids'),  # 生产参数
                    'uom_value_ids': in_params.get('uom_value_ids'),  # 生产单位
                }
                children = []
                for rec in in_params.get('children') or []:
                    if rec.get('material'):
                        children.append(rec)
                data['children'] = children
                upload_bom_list = [data]
                for res in in_params.get('children') or []:
                    if res.get('material'):
                        continue
                    upload_bom_list.extend(get_product_bom(res))
                return upload_bom_list

            boms = get_product_bom(params)
            # 开始检查基础信息 ( 预设BOM )
            for bom in boms:
                if bom.get('id'):
                    product_id = request.env['product.template'].browse(bom.get('id'))
                else:
                    product_id = request.env['product.template'].search(
                        [('default_code', '=', bom.get('product_code'))], limit=1)
                if not product_id:
                    continue
                presets = request.env['sb.presets.bom'].sudo().search(
                    [('product_id', '=', product_id.product_variant_id.id), ('state', '=', 'approved')], limit=1)
                if not presets:  # 不存在预设BOM则不检查差异
                    continue
                presets_diff_data = {
                    'product_id': product_id.product_variant_id.id,
                    'presets_bom_id': presets.id,
                }
                is_create_diff = False
                # 检查拼版
                uom_value_ids = bom.get('uom_value_ids')
                pnll_value = self.get_uom_value(uom_value_ids, 'PNLL')
                presets_diff_data['pnl_width_diff'] = pnll_value
                if pnll_value and float(pnll_value) != presets.pnl_width:
                    is_create_diff = True
                pnlr_value = self.get_uom_value(uom_value_ids, 'PNLR')
                presets_diff_data['pnl_length_diff'] = pnlr_value
                if pnlr_value and float(pnlr_value) != presets.pnl_length:
                    is_create_diff = True
                set_value = self.get_uom_value(uom_value_ids, 'SET')
                presets_diff_data['pcs_set_diff'] = set_value
                if set_value and float(set_value) != presets.pcs_set:
                    is_create_diff = True
                panel_value = self.get_uom_value(uom_value_ids, 'SHEET')
                presets_diff_data['pnl_diff'] = panel_value
                if panel_value and float(panel_value) != presets.pnl:
                    is_create_diff = True
                sheet_value = self.get_uom_value(uom_value_ids, 'PANEL')
                if set_value:
                    set_pnl = float(sheet_value) / float(set_value)
                    presets_diff_data['set_pnl_diff'] = set_pnl
                    if set_pnl and float(set_pnl) != presets.set_pnl:
                        is_create_diff = True
                else:
                    presets_diff_data['set_pnl_diff'] = 0
                    is_create_diff = True
                finished_categ = request.env.ref('sb_sale.finished_product_type')
                semi_categ = request.env.ref('sb_sale.semi_product_type')
                # JMI明细
                jmi_line_ids = []
                for line in bom.get('children'):
                    jmi_line_ids.append({
                        'default_code': line.get('product_code'),
                        'is_jmi': True,
                        'diff_qty': 0,
                        'panel_qty': float(line.get('panel_qty'))
                    })
                # 生成JMI提交内容
                jmi_line_ids_list = []
                for jmi in jmi_line_ids:
                    p = request.env['product.product'].sudo().search([('default_code', '=', jmi.get('default_code'))],
                                                                     limit=1)
                    jmi_line_ids_list.append((0, 0, {
                        'product_id': p.id,
                        'product_uom_id': p.uom_id.id,
                        'panel_qty': jmi.get('panel_qty'),
                    }))
                presets_diff_data['jmi_line_ids'] = jmi_line_ids_list
                presets_line_ids = []
                for line in presets.bom_line_ids:
                    if line.product_id.categ_id.id in [finished_categ.id, semi_categ.id]:
                        continue
                    presets_line_ids.append({
                        'default_code': line.default_code,
                        'is_presets': True,
                        'diff_qty': 0,
                        'origin_qty': line.product_qty,
                        'panel_qty': line.product_qty
                    })
                ext_line_ids = jmi_line_ids + presets_line_ids
                diff_line_ids = dict()
                for line in ext_line_ids:
                    if line.get('default_code') in diff_line_ids:  # 比较
                        is_jmi = line.get('is_jmi')
                        is_presets = line.get('is_presets')
                        dt = diff_line_ids[line.get('default_code')]
                        if not dt.get('is_jmi') and is_jmi:
                            is_jmi = True
                            diff_line_ids[line.get('default_code')].update({'is_jmi': is_jmi})
                        if not dt.get('is_presets') and is_presets:
                            is_presets = True
                            diff_line_ids[line.get('default_code')].update({'is_presets': is_presets})
                        # 比较数量差异
                        if line.get('panel_qty') != dt.get('panel_qty'):
                            diff_qty = abs(line.get('panel_qty') - dt.get('panel_qty'))
                            diff_line_ids[line.get('default_code')].update({'diff_qty': diff_qty})
                        if not diff_line_ids[line.get('default_code')].get('origin_qty'):
                            diff_line_ids[line.get('default_code')].update({'origin_qty': line.get('panel_qty')})
                    else:  # 新增
                        diff_line_ids[line.get('default_code')] = line
                sum_diff_qty = round(sum([v.get('diff_qty') for k, v in diff_line_ids.items()]), 4)
                if sum_diff_qty > 0 or is_create_diff:
                    diff = request.env['sb.presets.bom.diff'].search([('presets_bom_id', '=', presets.id)], limit=1)
                    presets_diff_line = []
                    for key, item in diff_line_ids.items():
                        p = request.env['product.product'].sudo().search([('default_code', '=', key)], limit=1)
                        presets_diff_line.append((0, 0, {
                            'product_id': p.id,
                            'panel_qty': item.get('panel_qty'),
                            'product_uom_id': p.uom_id.id,
                            'is_presets': item.get('is_presets'),
                            'is_jmi': item.get('is_jmi'),
                            'origin_qty': item.get('origin_qty'),
                            'diff_qty': round(item.get('diff_qty'), 6),
                        }))
                    presets_diff_data['line_ids'] = presets_diff_line
                    if diff:
                        diff.write({'line_ids': [(5,)]})  # 解除绑定
                        diff.write({'jmi_line_ids': [(5,)]})  # 解除绑定
                        diff.write(presets_diff_data)
                    else:
                        diff.create(presets_diff_data)
        params = self.remove_fields_recursive(params, fields)
        self.create_log_record('success', params, code)
        return code

    def remove_fields_recursive(self, data, fields_to_remove):
        if isinstance(data, dict):
            # 创建新字典，不包含要删除的字段
            new_dict = {}
            for key, value in data.items():
                if key not in fields_to_remove:
                    # 递归处理子元素
                    new_dict[key] = self.remove_fields_recursive(value, fields_to_remove)
            return new_dict
        elif isinstance(data, list):
            # 处理列表中的每个元素
            return [self.remove_fields_recursive(item, fields_to_remove) for item in data]
        else:
            # 基本类型直接返回
            return data

    # 定义一个函数，根据 key 获取值
    def get_uom_value(self, uom_value_ids, key):
        # 使用列表推导式查找匹配的字典项
        return next((item['value'] for item in uom_value_ids if item['uom_id'] == key), None)

    def recursion_checkout_bom(self, params_list, bom_id=False, materiel_status=False, application_id=False):
        """
        1. 先处理原材料相关操作, 原材料不创建, 返回提示, 退出本次创建
        1. 如果如果上传的id存在 需检查删除本次产品相关信息
            删除规则
                -查找相关bom, 如果存在引用则不删除, 更改为最新信息
                -不存在bom绑定, 则先删除bom_line关联的子类和产品
        materiel_status获取规则
            1. 先从上级往下传值获取
            2. 无值从参数获取
            3. 无值从产品中获取
            4. 以上都无值则为空
        """
        for params in params_list:
            product_name = params.get('product_name')
            materiel_status = materiel_status or params.get('materiel_status', False)  # 先从参数中获取
            if not product_name:
                return {'code': 1, 'msg': _('The product name cannot be empty'), 'data': False}
            product_code = params.get('product_code')
            if not product_code:
                return {'code': 1, 'msg': _('The product code cannot be empty'), 'data': False}
            # 成品分类
            finished_categ = request.env.ref('sb_sale.finished_product_type')
            semi_categ = request.env.ref('sb_sale.semi_product_type')
            panel_qty = params.get('panel_qty', 1)
            pcs_qty = params.get('pcs_qty', 1)
            material = params.get('material')
            pcs_id = request.env.ref('pcbmanufact.uom_part')
            if material:
                # 需排除成品和半成品的产品
                material_domain = [('categ_id', 'not in', [finished_categ.id, semi_categ.id]),
                                   '|', ('default_code', '=', product_code),
                                   ("product_variant_ids.default_code", "=", product_code)]
                product = request.env['product.template'].search(material_domain, limit=1)
                if not product:
                    return {
                        'code': 1,
                        'msg': _('Raw material: %s Code: %s Cannot be created, please enter the product code') % (
                            product_name, product_code),
                        'data': False
                    }
                if bom_id:
                    operation_code = params.get('operation_code')
                    if not operation_code:
                        return {'code': 1, 'msg': _('The operation code cannot be empty'), 'data': False}
                    routing = bom_id.operation_ids.filtered(lambda x: x.workcenter_id.code == operation_code)
                    if not routing:
                        return {
                            'code': 1,
                            'msg': _('The operation code: %s does not exist') % operation_code,
                            'data': False
                        }
                    self.bom_bond(bom_id, product, panel_qty, routing, categ_id=False)
                continue
            idx = params.get('id')
            if not params.get('product_version'):
                return {'code': 1, 'msg': _('成品,半成品版本不能为空'), 'data': False}
            if idx:
                # 成品
                product = request.env['product.template'].search([('id', '=', idx)])
                if product.s_mes_lock and product.categ_id.code == 'CHP':
                    return {
                        'code': 1,
                        'msg': _('产品: %s 编码: %s 已推送至MES, 无法进行更改') % (product_name, product_code),
                        'data': False
                    }
                materiel_status = materiel_status or product.materiel_status  # 参数没有从产品获取
                if not product:
                    return {'code': 1, 'msg': _('The product id does not exist'), 'data': False}
                # 搜索生产订单, 检查单位
                code = self.check_product_uom_ids(product, params)
                if code.get('code') != 200:
                    return code
                application_product_id = params.get('application_product_id', False)
                application = request.env['application.product'].search([('name', '=', application_product_id)],
                                                                        limit=1)
                if not application and not bom_id:
                    application = application.create({'name': application_product_id})
                application_id = application.id  # 成品需要先替代
                finished_dict = {
                    'application_product_id': application_id,
                    's_disable_down_card': True,
                    'name': product_name,
                    'materiel_status': materiel_status,
                    'jmi_author': params.get('jmi_author'),
                    'default_code': product_code
                }
                product.write(finished_dict)
                master_bom_id = product.bom_ids
                if product.bom_count > 1:
                    return {'code': 1, 'msg': _('The product has multiple BOMs and cannot be changed'), 'data': False}
                # 检查bom, 如果存在生产订单, bom无法修改
                # 检查生产订单, 如果存在生产订单, 则需要控制可修改部分
                if master_bom_id:
                    code = self.check_product_bom_id(master_bom_id, product, params)
                    if code.get('code') != 200:
                        return code
                # if master_bom_id:
                #     master_bom_id.bom_line_ids.unlink()
            else:
                domain = [('active', 'in', [True, False])]
                categ = finished_categ
                # 制造id
                manufacture = request.env.ref('mrp.route_warehouse0_manufacture')
                # 订单补货
                mto = request.env.ref('stock.route_warehouse0_mto')
                product_dict = {
                    'name': product_name,
                    'sale_ok': True,
                    'purchase_ok': True,
                    'active': True,
                    'tracking': 'lot',
                    'default_code': product_code,
                    'materiel_status': materiel_status,
                    'versions': params.get('product_version'),
                    'detailed_type': 'product',
                    # 默认单位 PCS, 默认路线  [按订单补给(MTO), 制造]
                    'uom_id': pcs_id.id,
                    'uom_po_id': pcs_id.id,
                    'jmi_author': params.get('jmi_author'),
                    'route_ids': [[4, manufacture.id], [4, mto.id]]
                }
                if not application_id:
                    # 成品应用必填
                    ap_name = params.get('application_product_id', False)
                    application = request.env['application.product'].search([('name', '=', ap_name)], limit=1)
                    if not application:
                        application = request.env['application.product'].create({'name': ap_name})
                    product_dict['application_product_id'] = application.id
                    application_id = application.id  # 半成品获取成品领域
                # 成品和半成品
                if not bom_id:  # 成品
                    domain.extend([('categ_id', '=', finished_categ.id)])
                else:
                    categ = semi_categ
                    domain.extend([('categ_id', '=', semi_categ.id)])
                domain += ['|', ('default_code', '=', product_code),
                           ("product_variant_ids.default_code", "=", product_code)]
                product_dict['categ_id'] = categ.id  # 添加分类
                if categ == finished_categ:
                    product_dict['s_disable_down_card'] = True
                product = request.env['product.template'].search(domain, limit=1)
                if product.s_mes_lock:
                    return {
                        'code': 1,
                        'msg': _('产品: %s 编码: %s 已推送至MES, 无法进行更改') % (product_name, product_code),
                        'data': False
                    }
                if not product:  # 需要创建成品和半成品
                    product = request.env['product.template'].create(product_dict)
                else:
                    # 成品,半成品单位控制
                    code = self.check_product_uom_ids(product, params)
                    if code.get('code') != 200:
                        return code
                    product.write({
                        'name': product_name,
                        'materiel_status': materiel_status,
                        'versions': params.get('product_version'),
                        'application_product_id': application_id
                    })
            # 阻抗表,开料表,叠构图 图片
            try:
                impedance_diagram = params.get('Impedance_diagram', '')
                if impedance_diagram:
                    product.write({'impedance_image': impedance_diagram})
            except Exception:
                _logger.error('阻抗图参数错误, 无法保存')
            try:
                stack_up_image = params.get('stack_up_image', '')
                if stack_up_image:
                    product.write({'stack_up_image': stack_up_image})
            except Exception:
                _logger.error('开料图参数错误, 无法保存')
            try:
                cutting_drawing_image = params.get('cutting_drawing_image', '')
                if cutting_drawing_image:
                    product.write({'cutting_drawing_image': cutting_drawing_image})
            except Exception:
                _logger.error('开料图参数错误, 无法保存')

            # ============================ 产品-履历 ============================
            s_mi_change_resume_list = params.get('s_mi_change_resume_ids', [])
            s_mi_change_resume_ids = []
            for s_mi_change_resume in s_mi_change_resume_list:
                s_mi_change_resume_ids.append((0, 0, {
                    'pn_rev': s_mi_change_resume.get('pn_rev', ''),
                    'change_text': s_mi_change_resume.get('change_text') or False,
                    'mi_engineer': s_mi_change_resume.get('mi_engineer') or False,
                    'change_level': s_mi_change_resume.get('change_level'),
                }))
            if s_mi_change_resume_ids:
                product.s_mi_change_resume_ids.unlink()
                product.write({'s_mi_change_resume_ids': s_mi_change_resume_ids})
            # ============================ 销售-客户 ============================
            customer_list = params.get('customer', [])
            customer_item_code_ids = []
            # customer_item_code = 1
            for customer in customer_list:
                name = customer.get('name', '')
                code = customer.get('code', '')
                if not name and not code:
                    continue
                domain = [('is_virtual_customer', '=', False), ('customer_rank', '>', 0)]
                if code:
                    domain.append(('code', '=', code))
                elif name:
                    domain.append(('name', '=', name))
                # price = customer.get('price', 0)
                # version = customer.get('version', '')
                # item_desc = customer.get('item_desc', '')
                res_partner = request.env['res.partner'].search(domain, limit=1)
                if not res_partner:
                    pricelist = request.env['product.pricelist'].search([], limit=1, order='id asc')
                    res_partner_dict = {
                        'name': name,
                        'code': code,
                        'customer_rank': 1,
                        'property_product_pricelist': pricelist.id,
                    }
                    res_partner = request.env['res.partner'].create(res_partner_dict)
                # 搜索客户编码
                domain_2 = [('product_id', '=', product.id), ('customer_id', '=', res_partner.id)]
                customer_item = request.env['customer.item.code'].search(domain_2)
                if customer_item:
                    # customer_item = request.env['customer.item.code'].create({
                    #     'product_id': product.id,
                    #     'customer_id': res_partner.id,
                    #     'code': Pn,
                    #     'customer_item_desc': customer.get('item_desc', ''),
                    #     'version': customer.get('version', ''),
                    #     'recent_price': customer.get('price', 0)
                    # })
                    # customer_item_code += 1
                    # return {'code': 1, 'msg': _('客户产品未找到客户商品编码, 请在ERP后台添加客户产品编码'), 'data': False}
                    customer_item_code_ids.append(customer_item.id)
            if customer_item_code_ids:
                # 先清空原有的客户编码
                # product.customer_item_code_ids.unlink()
                product.write({'customer_item_code_ids': [(6, 0, customer_item_code_ids)]})
            # ============================ 产品单位 ============================
            uom_value_list = params.get('uom_value_ids', [])
            uom_value_ids = []
            for uom in uom_value_list:
                uom_name = uom.get('uom_id', False)
                if not uom_name:
                    continue
                value = uom.get('value', 0)
                uom_id = request.env['uom.uom'].search(
                    [('category_id.category_type', '=', 'prod'), ('name', '=', str(uom_name))], limit=1)
                if not uom_id:
                    return {'code': 1, 'msg': _(
                        'Product unit: %s does not exist and needs to be created in the background!') % uom_name}
                uom_value_ids.append((0, 0, {
                    'uom_id': uom_id.id,
                    'uom_value': value
                }))
            if uom_value_ids:
                product.uom_value_ids.unlink()
                product.write({'uom_value_ids': uom_value_ids})
                # 查看是否存在单位 PCS , 如不存在添加
                uom_pcs = product.uom_value_ids.filtered(lambda x: x.uom_id.id == pcs_id.id)
                if len(uom_pcs) == 1 and uom_pcs.uom_value == 0:
                    uom_pcs.write({'uom_value': 1})
                if len(uom_pcs) > 1:
                    uom_pcs[1:].unlink()  # 保留第一条, 删除多余的
                    uom_pcs.update({'uom_value': 1})  # 重新赋值为1
                if not uom_pcs:  # 没有添加一条
                    product.write({'uom_value_ids': [(0, 0, {
                        'uom_id': pcs_id.id,
                        'uom_value': 1
                    })]})
            # ============================ 生产参数 ============================
            param_line_list = params.get('param_line_ids', [])
            param_line_ids = self.get_attribute_value(param_line_list, 'param')
            if param_line_ids:
                product.param_line_ids.unlink()
                product.write({'param_line_ids': param_line_ids})
            # ============================ 生产规格 ============================
            spec_line_list = params.get('spec_line_ids', [])
            spec_line_ids = self.get_attribute_value(spec_line_list, 'spec')
            if spec_line_ids:
                product.spec_line_ids.unlink()
                product.write({'spec_line_ids': spec_line_ids})
            # ============================ 采购参数 ============================
            pur_param_line_list = params.get('pur_param_line_ids', [])
            pur_param_line_ids = self.get_attribute_value(pur_param_line_list, 'pur')
            if pur_param_line_ids:
                product.pur_param_line_ids.unlink()
                product.write({'pur_param_line_ids': pur_param_line_ids})
            # ============================ 阻抗表参数 ============================
            impedance_list = params.get('impedance_ids') or []
            impedance_ids = []
            for impedance in impedance_list:
                imp = request.env['product.impedance'].create(impedance)
                impedance_ids.append(imp.id)
            if impedance_ids:
                product.write({'impedance_ids': [(5,)]})
                product.write({'impedance_ids': [(6, 0, impedance_ids)]})
            # ============================ PP 统计 ============================
            statistics_ids = []
            statistics_list = params.get('statistics_ids', [])
            for statistics in statistics_list:
                statistics_id = request.env['sb.pp.statistics'].create(statistics)
                statistics_ids.append(statistics_id.id)
            if statistics_ids:
                product.write({'s_statistics_ids': [(5,)]})
                product.write({'s_statistics_ids': [(6, 0, statistics_ids)]})
            # ============================ BOM 创建/修改 ============================
            if bom_id:  # 产品跟bom绑定
                operation_code = params.get('operation_code') or 'empty'
                # if not operation_code:
                #     return {'code': 1, 'msg': _('The operation code cannot be empty'), 'data': False}
                routing = bom_id.operation_ids.filtered(lambda x: x.workcenter_id.code == operation_code)
                # if not routing:
                #     return {
                #         'code': 1,
                #         'msg': _('The operation code: %s does not exist') % operation_code,
                #         'data': False
                #     }
                self.bom_bond(bom_id, product, panel_qty, routing, categ_id=semi_categ.id)
            if params.get('children', []):
                bom_dict = {
                    'product_tmpl_id': product.id,
                    'code': product.default_code,
                    'product_uom_id': product.uom_id.id,
                    'type': 'normal',
                    # 'bom_id': bom_id.id if bom_id else False,
                }
                if product.bom_count > 1:
                    return {'code': 1, 'msg': _('Product: %s multi BOM, can not be changed!') % product.name,
                            'data': False}
                new_bom_id = product.bom_ids
                if not new_bom_id:
                    new_bom_id = request.env['mrp.bom'].create(bom_dict)
                else:
                    code = self.check_product_bom_id(new_bom_id, product, params)
                    if code.get('code') != 200:
                        return code
                operation_list = params.get('operation_ids', [])
                operation_ids = []
                step_list = []
                for operation in operation_list:  # 作业
                    operation_name = operation.get('operation_name', '')
                    operation_code = operation.get('operation_code', '')
                    if operation_code == 'LOC01':  # 需求讨论, 开卡不需要, 直接略过
                        continue
                    step = operation.get('step', 1)
                    if step in step_list:
                        continue
                    step_list.append(step)
                    time_mode = operation.get('time_mode', 'manual')
                    time_cycle_manual = operation.get('time_sycle_manual', 60)
                    employee_ratio = operation.get('employee_ratio', 1)
                    is_drill_operation = operation.get('is_drill_operation', False)
                    is_back_drill_operation = operation.get('is_back_drill_operation', False)
                    drill_depth_program = operation.get('drill_depth_program', '')  # 控深钻程式
                    drill_unit = operation.get('drill_unit', '')  # drill Unit
                    drill_hole_total = operation.get('drill_hole_total', 0)  # 钻孔总孔数
                    parameters_ids_list = operation.get('parameters_ids')
                    workcenter_id = request.env['mrp.workcenter'].search([('code', '=', operation_code)], limit=1)
                    if not workcenter_id:  # 工作中心
                        return {'code': 1, 'msg': _(
                            'Work Center: %s does not exist, need to be created in the background!') % operation_code}
                        # workcenter_dict = {
                        #     'name': operation_name,
                        #     'code': operation_code,
                        # }
                        # workcenter_id = request.env['mrp.workcenter'].create(workcenter_dict)
                    routing_workcenter_dict = {
                        'name': workcenter_id.name,
                        'workcenter_id': workcenter_id.id,
                        'bom_id': new_bom_id.id,
                        'step': step,
                        'time_mode': time_mode,
                        'time_cycle_manual': time_cycle_manual,
                        'employee_ratio': employee_ratio,
                        'is_drill_operation': is_drill_operation,
                        'is_back_drill_operation': is_back_drill_operation,
                        'drill_depth_program': drill_depth_program,
                        'drill_unit': drill_unit,
                        'drill_hole_total': drill_hole_total,
                    }
                    # 作业明细行
                    routing_workcenter_id = request.env['mrp.routing.workcenter'].create(routing_workcenter_dict)
                    # 工序参数
                    # parameters_ids = self.get_attribute_value(parameters_ids_list, 'route')
                    parameters_ids = []
                    for param_line in parameters_ids_list:
                        attribute_id = param_line.get('attribute_id', '')
                        attr_id = request.env['product.attribute'].search([('limit_type', '=', 'route'),
                                                                           ('code', '=', attribute_id.strip())],
                                                                          limit=1)
                        if not attr_id:
                            return {'code': 1, 'msg': _(
                                'Process parameter: %s does not exist, need to be created in the background!') % attribute_id,
                                    'data': False}
                        value_id = param_line.get('value_id', '')
                        val_id = False
                        if value_id:
                            val_id = attr_id.value_ids.filtered(lambda x: x.name == str(value_id).strip())
                            if not val_id:
                                val_id = request.env['product.attribute.value'].create({
                                    'attribute_id': attr_id.id,
                                    'name': str(value_id).strip()
                                })
                        parameters_ids.append((0, 0, {
                            'attribute_id': attr_id.id,
                            'thai_name': param_line.get('thai_name', ''),
                            'eng_name': param_line.get('eng_name', ''),
                            'value_id': val_id[0].id if val_id else False,
                            'z_thai_name': param_line.get('z_thai_name', ''),
                            'z_eng_name': param_line.get('z_eng_name', ''),
                        }))
                    if parameters_ids:
                        routing_workcenter_id.param_line_ids.action_archive()
                        routing_workcenter_id.write({'param_line_ids': parameters_ids})
                    if is_drill_operation:
                        try:
                            # 钻孔图
                            drill_image = operation.get('drill_image', '')
                            if drill_image:
                                routing_workcenter_id.write({
                                    'drill_image': drill_image,
                                })
                        except Exception:
                            _logger.error('钻孔图参数错误, 无法保存')
                        drill_operation_ids_list = operation.get('drill_operation_ids', [])
                        drill_operation_ids = []
                        for drill in drill_operation_ids_list:
                            drill_id = request.env['mrp.routing.workcenter.drill'].create(drill)
                            drill_operation_ids.append(drill_id.id)
                        if drill_operation_ids:
                            routing_workcenter_id.drill_operation_ids.action_archive()
                            routing_workcenter_id.write({'drill_operation_ids': [(6, 0, drill_operation_ids)]})
                    if is_back_drill_operation:
                        back_drill_operation_ids_list = operation.get('back_drill_operation_ids', [])
                        back_drill_ids = []
                        for back_drill in back_drill_operation_ids_list:
                            back_drill_id = request.env['mrp.routing.workcenter.back.drill'].create(back_drill)
                            back_drill_ids.append(back_drill_id.id)
                        if back_drill_ids:
                            routing_workcenter_id.back_drill_operation_ids.action_archive()
                            routing_workcenter_id.write({'back_drill_operation_ids': [(6, 0, back_drill_ids)]})
                    operation_ids.append(routing_workcenter_id.id)
                if operation_ids:
                    new_bom_id.write({'operation_ids': [(6, 0, operation_ids)]})
                code = self.recursion_checkout_bom(
                    params_list=params.get('children', []),
                    bom_id=new_bom_id,
                    materiel_status=materiel_status,
                    application_id=application_id)
                if code.get('code') != 200:
                    return code
        return {'code': 200, 'msg': '', 'data': True}

    @classmethod
    def bom_bond(cls, bom_id, product, panel_qty, routing, categ_id=False):
        bom_line_id = bom_id.bom_line_ids.filtered(lambda x: x.product_id.id == product.product_variant_id.id)
        if bom_line_id:
            # 半成品则pcs_qty为1, 不传panel_qty
            if product.categ_id.id == categ_id:
                bom_line_id[0].write({
                    'operation_id': routing.id,
                    'product_qty': 1,
                })
            else:
                bom_line_id[0].write({
                    'operation_id': routing.id,
                    'panel_qty': panel_qty,
                })
        else:
            bom_line = request.env['mrp.bom.line'].create({
                'bom_id': bom_id.id,
                'operation_id': routing.id,
                'product_id': product.product_variant_id.id,
            })
            if product.categ_id.id == categ_id:
                bom_line.write({
                    'operation_id': routing.id,
                    'product_qty': 1,
                })
            else:
                bom_line.write({
                    'operation_id': routing.id,
                    'panel_qty': panel_qty,
                })
            # bom_id.write({
            #     'bom_line_ids': [(0, 0, {
            #         'product_id': product.product_variant_id.id,
            #         'panel_qty': panel_qty,
            #     })]
            # })

    @classmethod
    def get_product_category(cls, params=None):
        """
        """
        # 指定成品类
        return request.env.ref('sb_sale.finished_product_type')
        # if params is None:
        #     params = {}
        # if not params:
        #     return request.env['product.category']
        # product_categ = params.get('product_categ_id')
        # categ = request.categ = request.env['product.category'].search([('name', '=', product_categ)], limit=1)
        # if not categ:
        #     categ = request.env['product.category'].create({'name': product_categ})
        # return categ

    @classmethod
    def get_attribute_value(cls, params_line_list, types):
        param_line_ids = []
        for param_line in params_line_list:
            attribute_id = param_line.get('attribute_id', '')
            attr_id = request.env['product.attribute'].search([('limit_type', '=', types),
                                                               ('name', '=', attribute_id.strip())], limit=1)
            if not attr_id:
                attr_id = request.env['product.attribute'].create({'name': attribute_id.strip(), 'limit_type': types})
            value_id = param_line.get('value_id', '')
            val_id = False
            if value_id:
                val_id = attr_id.value_ids.filtered(lambda x: x.name == str(value_id).strip())
                if not val_id:
                    val_id = request.env['product.attribute.value'].create({
                        'attribute_id': attr_id.id,
                        'name': str(value_id).strip()
                    })
            param_line_ids.append((0, 0, {
                'attribute_id': attr_id.id,
                'thai_name': param_line.get('thai_name', ''),
                'eng_name': param_line.get('eng_name', ''),
                'value_id': val_id[0].id if val_id else False,
                'z_thai_name': param_line.get('z_thai_name', ''),
                'z_eng_name': param_line.get('z_eng_name', ''),
            }))
        return param_line_ids

    @classmethod
    def check_product_uom_ids(cls, product, params):
        # 搜索生产订单
        production_id = request.env['mrp.production'].search([('product_tmpl_id', '=', product.id),
                                                              # ('state', 'not in', ['done', 'cancel'])
                                                              ], limit=1)
        if production_id:
            return {'code': 1, 'msg': _('产品: %s 已进行投料, 投料单: %s, 更改产品信息请走ECN!') % (
                product.name, production_id.name), 'data': False}
            # 成品单位控制, PART,SET,PANEL,不能变
            part = None
            _set = None
            panel = None
            for uom in params.get('uom_value_ids') or []:
                if uom.get('uom_id') == 'PART':
                    part = uom
                elif uom.get('uom_id') == 'SET':
                    _set = uom
                elif uom.get('uom_id') == 'PANEL':
                    panel = uom
            if part:
                p = product.uom_value_ids.filtered(lambda x: x.uom_id.name == 'PART')
                if round(p.uom_value, 3) != round(float(part.get('value')), 3):
                    return {'code': 1, 'msg': _(
                        'Product: %s is in production, production order: %s, PART unit cannot be modified!') % (
                                                  product.name, production_id.name), 'data': False}
            if _set:
                s = product.uom_value_ids.filtered(lambda x: x.uom_id.name == 'SET')
                if round(s.uom_value, 3) != round(float(_set.get('value')), 3):
                    return {'code': 1, 'msg': _(
                        'product: %s is in production, production order: %s, SET unit cannot be modified!') % (
                                                  product.name, production_id.name), 'data': False}
            if panel:
                p = product.uom_value_ids.filtered(lambda x: x.uom_id.name == 'PANEL')
                if round(p.uom_value, 3) != round(float(panel.get('value')), 3):
                    return {'code': 1, 'msg': _(
                        'product: %s is in production, production order: %s, PANEL unit cannot be modified!') % (
                                                  product.name, production_id.name), 'data': False}
        return {'code': 200, 'msg': '', 'data': True}

    @classmethod
    def check_product_bom_id(cls, bom_id, product, params):
        if not bom_id:
            return {'code': 200, 'msg': '', 'data': True}
        domain = [('product_id', '=', product.product_variant_id.id),
                  ('bom_id', '=', bom_id.id),
                  # ('state', 'not in', ['done', 'cancel'])
                  ]
        production_id = request.env['mrp.production'].search(domain, limit=1)
        if production_id:
            return {'code': 1, 'msg': _('产品: %s 已进行投料, 投料单: %s, 更改产品信息请走ECN!') % (
                product.name, production_id.name), 'data': False}
            # 1. 判断数量是否相等
            if len(bom_id.bom_line_ids) != len(params.get('children') or []):
                return {'code': 1, 'msg': _(
                    f'Product: %s The associated BOM has been scheduled for production. Production Order: %s. Uploaded BOM details cannot be modified!') % (
                                              product.name, production_id.name), 'data': False}
            # 不可替换bom组件产品
            bom_line_product_list_ids = []
            for bom_line in bom_id.bom_line_ids:
                bom_line_product_list_ids.append(bom_line.product_id.id)
            params_line_product_list_ids = []
            for child in params.get('children'):
                domain = [('default_code', '=', child.get('product_code'))]
                pd = request.env['product.template'].search(domain, limit=1)
                params_line_product_list_ids.append(pd.product_variant_id.id)
            # 排序组合转字符串后比对
            if sorted(bom_line_product_list_ids) != sorted(params_line_product_list_ids):
                return {'code': 1, 'msg': _(
                    'Product: %s The associated BOM has been scheduled for production. Production Order: %s. BOM details cannot be changed!') % (
                                              product.name, production_id.name), 'data': False}
            # 2. 检查作业step工序是否匹配, 如有错乱或缺失不可修改
            operation_list_ids_1 = []
            for oper in params.get('operation_ids'):
                s = '%02d' % oper.get('step')
                stt = s + '.' + '[' + oper.get('operation_code', '') + ' ] ' + oper.get('operation_name', '')
                operation_list_ids_1.append(stt)
            operation_list_ids_2 = []
            for oper in bom_id.operation_ids:
                stt = oper.name
                operation_list_ids_2.append(stt)
            if sorted(operation_list_ids_1) != sorted(operation_list_ids_2):
                return {'code': 1, 'msg': _(
                    'Product: %s The associated BOM has been scheduled for production. Production Order: %s. scheduling process does not match the changing process, cannot be modified!') % (
                                              product.name, production_id.name), 'data': False}
            #
        else:
            bom_id.bom_line_ids.unlink()
        return {'code': 200, 'msg': '', 'data': True}

    # 物料销售单退审批
    @http.route('/api/sale/order/approve', type='json', auth='user', methods=['POST'], csrf=False)
    def sale_order_approve(self, **params):
        """
        退审批：
        当计划为未审批通过时, 则自动将工程定单审批变未审批状态, 接口返回退审成功状态
        如已经是计划审批完成, 接口返回退审失败状态
        """
        params = request.params
        product_code = params.get('product_code')
        product_template = request.env['product.template'].search([('default_code', '=', product_code)], limit=1)
        if not product_template:
            result = {'code': 1, 'msg': _('The product code does not exist'), 'status': ''}
            self.create_log_record('fail', params, result)
            return result
        order_line = request.env['sale.order.line'].search([('product_template_id', '=', product_template.id)], limit=1)
        if order_line:
            order_id = order_line.order_id
            sb_workflow_item_ids = order_id.sb_workflow_item_ids
            # 当前工程审批状态
            status = sb_workflow_item_ids.filtered(lambda x: x.name == '工程审批').mapped('state')
            if sb_workflow_item_ids and order_id.sb_workflow_state == 'approve':
                result = {'code': 400, 'msg': '订单审批通过, 无法退审', 'status': status[0] if status else ''}
                self.create_log_record('fail', params, result)
                return result
            if sb_workflow_item_ids and order_id.sb_workflow_state == 'approval':
                ob_sb_workflow = sb_workflow_item_ids.filtered(lambda x: x.state == 'pass' and x.name == '工程审批')
                if not ob_sb_workflow:
                    result = {'code': 400, 'msg': '工程审批未有通过, 无法退审', 'status': status[0] if status else ''}
                    self.create_log_record('fail', params, result)
                    return result
                pl_sb_workflow = sb_workflow_item_ids.filtered(lambda x: x.state == 'pass' and x.name == '计划审批')
                if pl_sb_workflow:
                    result = {'code': 400, 'msg': '计划审批已完成,此单不可退审', 'status': status[0] if status else ''}
                    self.create_log_record('fail', params, result)
                    return result
                # 需要将工程审批重置为审批中
                ob_sb_workflow.update({
                    'state': 'approval',
                    'user_id': request.env.user.id,
                    'approval_instructions': '退审',
                    'approval_time': datetime.today()
                })
                # 计划审批置为待审批
                sb_workflow_item_ids.filtered(lambda x: x.name == '计划审批').update({'state': 'wait_forward'})
                result = {'code': 200, 'msg': '退审成功', 'status': 'approval'}
                self.create_log_record('success', params, result)
                return result
            else:
                result = {'code': 400, 'msg': '产品订单未发起审批', 'status': 'draft'}
                self.create_log_record('fail', params, result)
                return result
        else:
            result = {'code': 400, 'msg': '产品未创建销售审批单', 'status': ''}
            self.create_log_record('fail', params, result)
            return result

    def create_log_record(self, state, api_params, result_json):
        api_records = {
            'api_params': api_params,
            'api_user_id': False,
            'api_route': request.httprequest.full_path,
            'state': state,
            'result_json': result_json
        }
        request.env['api.records'].sudo().create(api_records)
